--- /dev/null
+ostree_switch_root
--- /dev/null
+From e9cb0f0c6ef13773c823610f27a562bd54b2acd3 Mon Sep 17 00:00:00 2001
+From: Colin Walters <walters@verbum.org>
+Date: Mon, 24 Oct 2011 22:01:17 -0400
+Subject: [PATCH] Support OSTree
+
+See http://git.gnome.org/browse/ostree
+---
+ modules.d/95rootfs-block/mount-root.sh | 12 ++++++------
+ modules.d/99base/init | 32 ++++++++++++++++++++++----------
+ 2 files changed, 28 insertions(+), 16 deletions(-)
+
+diff --git a/modules.d/95rootfs-block/mount-root.sh b/modules.d/95rootfs-block/mount-root.sh
+index 2c89431..4f411da 100755
+--- a/modules.d/95rootfs-block/mount-root.sh
++++ b/modules.d/95rootfs-block/mount-root.sh
+@@ -34,8 +34,8 @@ mount_root() {
+
+ READONLY=
+ fsckoptions=
+- if [ -f "$NEWROOT"/etc/sysconfig/readonly-root ]; then
+- . "$NEWROOT"/etc/sysconfig/readonly-root
++ if [ -f "$NEWROOT_PREFIX"/etc/sysconfig/readonly-root ]; then
++ . "$NEWROOT_PREFIX"/etc/sysconfig/readonly-root
+ fi
+
+ if getargbool 0 "readonlyroot=" -y readonlyroot; then
+@@ -57,7 +57,7 @@ mount_root() {
+ if [ -f "$NEWROOT"/forcefsck ] || getargbool 0 forcefsck ; then
+ fsckoptions="-f $fsckoptions"
+ elif [ -f "$NEWROOT"/.autofsck ]; then
+- [ -f "$NEWROOT"/etc/sysconfig/autofsck ] && . "$NEWROOT"/etc/sysconfig/autofsck
++ [ -f "$NEWROOT_PREFIX"/etc/sysconfig/autofsck ] && . "$NEWROOT_PREFIX"/etc/sysconfig/autofsck
+ if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then
+ AUTOFSCK_OPT="$AUTOFSCK_OPT -f"
+ fi
+@@ -73,8 +73,8 @@ mount_root() {
+ rootopts=
+ if getargbool 1 rd.fstab -n rd_NO_FSTAB \
+ && ! getarg rootflags \
+- && [ -f "$NEWROOT/etc/fstab" ] \
+- && ! [ -L "$NEWROOT/etc/fstab" ]; then
++ && [ -f "$NEWROOT_PREFIX/etc/fstab" ] \
++ && ! [ -L "$NEWROOT_PREFIX/etc/fstab" ]; then
+ # if $NEWROOT/etc/fstab contains special mount options for
+ # the root filesystem,
+ # remount it with the proper options
+@@ -89,7 +89,7 @@ mount_root() {
+ rootopts=$opts
+ break
+ fi
+- done < "$NEWROOT/etc/fstab"
++ done < "$NEWROOT_PREFIX/etc/fstab"
+
+ rootopts=$(filter_rootopts $rootopts)
+ fi
+diff --git a/modules.d/99base/init b/modules.d/99base/init
+index 36b2152..9636990 100755
+--- a/modules.d/99base/init
++++ b/modules.d/99base/init
+@@ -275,6 +275,13 @@ unset queuetriggered
+ unset main_loop
+ unset RDRETRY
+
++ostree=$(getarg ostree=)
++if test -n $ostree; then
++ NEWROOT_PREFIX=${NEWROOT}/ostree/$ostree
++else
++ NEWROOT_PREFIX=$NEWROOT
++fi
++
+ # pre-mount happens before we try to mount the root filesystem,
+ # and happens once.
+ getarg 'rd.break=pre-mount' 'rdbreak=pre-mount' && emergency_shell -n pre-mount "Break pre-mount"
+@@ -287,14 +294,15 @@ getarg 'rd.break=mount' 'rdbreak=mount' && emergency_shell -n mount "Break mount
+ i=0
+ while :; do
+ if ismounted "$NEWROOT"; then
+- usable_root "$NEWROOT" && break;
++ echo "Checking usable $NEWROOT_PREFIX"
++ usable_root "$NEWROOT_PREFIX" && break;
+ umount "$NEWROOT"
+ fi
+ for f in $hookdir/mount/*.sh; do
+ [ -f "$f" ] && . "$f"
+ if ismounted "$NEWROOT"; then
+- usable_root "$NEWROOT" && break;
+- warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook"
++ usable_root "$NEWROOT_PREFIX" && break;
++ warn "$NEWROOT_PREFIX has no proper rootfs layout, ignoring and removing offending mount hook"
+ umount "$NEWROOT"
+ rm -f "$f"
+ fi
+@@ -320,11 +328,11 @@ unset __usr_found
+ for i in "$(getarg real_init=)" "$(getarg init=)" /sbin/init /etc/init /init /bin/sh; do
+ [ -n "$i" ] || continue
+
+- __p=$(readlink -m "$NEWROOT$i")
++ __p=$(readlink -m "$NEWROOT_PREFIX$i")
+ if [ -n "$__p" ] \
+ && [ "x$__usr_found" = "x" ] \
+ && [ ! -x "$__p" ] \
+- && strstr "$__p" "$NEWROOT/usr" \
++ && strstr "$__p" "$NEWROOT_PREFIX/usr" \
+ ; then
+ # we have to mount /usr
+ while read dev mp fs opts rest; do
+@@ -333,14 +341,14 @@ for i in "$(getarg real_init=)" "$(getarg init=)" /sbin/init /etc/init /init /bi
+ __usr_found="1"
+ break
+ fi
+- done < "$NEWROOT/etc/fstab" >> /etc/fstab
++ done < "$NEWROOT_PREFIX/etc/fstab" >> /etc/fstab
+ if [ "x$__usr_found" != "x" ]; then
+ info "Mounting /usr"
+- mount "$NEWROOT/usr" 2>&1 | vinfo
++ mount "$NEWROOT_PREFIX/usr" 2>&1 | vinfo
+ fi
+ fi
+
+- __p=$(readlink -f "$NEWROOT$i")
++ __p=$(readlink -f "$NEWROOT_PREFIX$i")
+ if [ -x "$__p" ]; then
+ INIT="$i"
+ break
+@@ -445,7 +453,11 @@ info "Switching root"
+ unset PS4
+
+ CAPSH=$(command -v capsh)
+-SWITCH_ROOT=$(command -v switch_root)
++if test -n "${ostree}"; then
++ SWITCH_ROOT=$(command -v ostree_switch_root)
++else
++ SWITCH_ROOT=$(command -v switch_root)
++fi
+ PATH=$OLDPATH
+ export PATH
+
+@@ -463,7 +475,7 @@ if [ -f /etc/capsdrop ]; then
+ }
+ else
+ unset RD_DEBUG
+- exec $SWITCH_ROOT "$NEWROOT" "$INIT" $initargs || {
++ exec $SWITCH_ROOT "$NEWROOT" $ostree "$INIT" $initargs || {
+ warn "Something went very badly wrong in the initramfs. Please "
+ warn "file a bug against dracut."
+ emergency_shell
+--
+1.7.6.4
+
--- /dev/null
+
+Experimenting with multiple roots
+---------------------------------
+
+ $ mkdir gnomeos-chroot
+ $ qemu-img create gnomeos.raw 2G
+ $ mkfs.ext2 -F gnomeos.raw
+ $ mount -o loop gnomeos.raw gnomeos-chroot
+ $ debootstrap --arch=amd64 squeeze gnomeos-chroot
+
+<http://wiki.debian.org/QEMU#Setting_up_a_testing.2BAC8-unstable_system>
+
+Follow the steps for making a disk image, downloading the business
+card CD, booting it in QEMU and running through the installer. Note I
+used the QCOW format, since it is more efficient. Here are the steps
+I chose:
+
+ $ qemu-img create -f qcow2 debian.qcow 2G
+ $ qemu-kvm -hda debian.qcow -cdrom debian-testing-amd64-businesscard.iso -boot d -m 512
+
+Test that the image works after installation too, before you start
+modifying things below! Remember to remove the -cdrom and -boot
+options from the installation QEMU command. It should just look like
+this:
+
+$ qemu-kvm -hda debian.qcow -m 512
+
+
+Modifying the image
+-------------------
+
+You now have a disk image in debian.img, and the first partition
+should be ext4.
+
+The first thing I did was mount the image, and move the "read only"
+parts of the OS to a new directory "r0".
+
+ $ mkdir /mnt/debian
+ $ modprobe nbd max_part=8
+ $ qemu-nbd --connect=/dev/nbd0 debian.qcow
+ $ mount /dev/nbd0p1 /mnt/debian/
+ $ cd /mnt/debian
+ $ mkdir r0
+ $ DIRS="bin dev etc lib lib32 lib64 media mnt opt proc root run sbin selinux srv sys tmp usr"
+ $ mv $DIRS r0
+ $ mkdir r0/{boot,var,home}
+ $ touch r0/{boot,var,home}/EMPTY
+
+Note that /boot, /home and /var are left shared; we create empty
+destination directories that will be mounted over. Now with it still
+mounted, we need to move on to the next part - modifying the initrd.
+
+Then I started hacking on the initrd, making understand how to chroot
+to "r0". I ended up with two patches - one to util-linux, and one to
+the "init" script in Debian's initrd.
+
+See:
+ 0001-switch_root-Add-subroot-option.patch
+ 0001-Add-support-for-subroot-option.patch
+
+$ git clone --depth=1 git://github.com/karelzak/util-linux.git
+$ cd util-linux
+$ patch -p1 -i ../0001-switch_root-Add-subroot-option.patch
+$ ./autogen.sh; ./configure ; make
+
+Now you have a modified "sys-utils/switch_root" binary. Let's next
+patch the initrd and rebuild it:
+
+$ cd ..
+
+Make a backup:
+
+ $ mkdir initrd
+ $ cp /mnt/debian/boot/initrd.img-3.0.0-1-amd64{,.orig}
+
+Unpack, and patch:
+
+ $ zcat /mnt/debian/boot/initrd.img-3.0.0-1-amd64 | (cd initrd; cpio -d -i -v)
+ $ (cd initrd && patch -p1 -i ../0001-Add-support-for-subroot-option.patch)
+
+Repack:
+
+ $ (cd initrd; find | cpio -o -H newc) | gzip > /mnt/debian/boot/initrd.img-3.0.0-1-amd64.new
+ $ mv /mnt/debian/boot/initrd.img-3.0.0-1-amd64{.new,}
+
+Unmount:
+
+ $ umount /mnt/debian
+
+Running hacktree inside the system
+----------------------------------
+
+This means that after booting, every process would be in /r0 -
+including any hacktree process. Assuming objects live in say
+/objects, we need some way for hacktree to switch things. I think
+just chroot breakout would work. This has the advantage the daemon
+can continue to use libraries from the active host.
+
+Note there is a self-reference here (as is present in Debian/Fedora
+etc.) - the update system would at present be shipped with the system
+itself. Should they be independent? That has advantages and
+disadvantages. I think we should just try really really hard to avoid
+breaking hacktree in updates.
+
--- /dev/null
+#!/bin/sh
+# This script sets up things we want to ship with the OS tree. It should
+# NOT set up caches. For example, do NOT run ldconfig in here.
+
+set -e
+set -x
+
+echo gnomeos >./etc/hostname
+
+cat >./etc/default/locale <<EOF
+LANG="en_US.UTF-8"
+EOF
+
+cp -p ./usr/share/sysvinit/inittab ./etc/inittab
+cp -p ./usr/share/base-files/nsswitch.conf ./etc/nsswitch.conf
+
+cat >./etc/pam.d/common-account <<EOF
+account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
+account requisite pam_deny.so
+account required pam_permit.so
+EOF
+cat >./etc/pam.d/common-auth <<EOF
+auth [success=1 default=ignore] pam_unix.so nullok_secure
+auth requisite pam_deny.so
+auth required pam_permit.so
+EOF
+cat >./etc/pam.d/common-password <<EOF
+password [success=1 default=ignore] pam_unix.so obscure sha512
+password requisite pam_deny.so
+password required pam_permit.so
+EOF
+cat >./etc/pam.d/common-session <<EOF
+session [default=1] pam_permit.so
+session requisite pam_deny.so
+session required pam_permit.so
+session required pam_unix.so
+EOF
+
+# base-passwd
+cp -p ./usr/share/base-passwd/passwd.master ./etc/passwd
+cp -p ./usr/share/base-passwd/group.master ./etc/group
+
+# Service rc.d defaults
+setuprc () {
+ name=$1
+ shift
+ type=$1
+ shift
+ priority=$1
+ shift
+
+ for x in $@; do
+ ln -s ../init.d/$name ./etc/rc${x}.d/${type}${priority}${name}
+ done
+}
+
+setuprc rsyslog S 10 2 3 4 5
+setuprc rsyslog S 30 0 6
+setuprc rsyslog K 90 1
+setuprc cron S 89 2 3 4 5
--- /dev/null
+#!/bin/sh
+# -*- indent-tabs-mode: nil; -*-
+# Install built ostree image to current system
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+set -x
+
+SRCDIR=`dirname $0`
+WORKDIR=`pwd`
+
+OBJ=gnomeos-initrd.img
+if ! test -f ${OBJ}; then
+ echo "Error: couldn't find '$OBJ'. Run gnomeos-make-image.sh"
+ exit 1
+fi
+
+if test -d /ostree; then
+ echo "/ostree already exists"
+ exit 1
+fi
+
+cp -a gnomeos-fs/ostree /
+initrd=`readlink gnomeos-initrd.img`
+cp ${initrd} /boot
+grubby --title "GNOME OS" --add-kernel=$kernel --copy-default --initrd=/boot/${initrd}
--- /dev/null
+#!/bin/sh
+# -*- indent-tabs-mode: nil; -*-
+# Generate a root filesystem image
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+set -x
+
+SRCDIR=`dirname $0`
+WORKDIR=`pwd`
+
+DEPENDS="debootstrap"
+
+for x in $DEPENDS; do
+ if ! command -v $x; then
+ cat <<EOF
+Couldn't find required dependency $x
+EOF
+ exit 1
+ fi
+done
+
+if test $(id -u) == 0; then
+ echo "Should not run this script as root."
+ exit 1
+fi
+
+if test -z "${OSTREE}"; then
+ OSTREE=`command -v ostree || true`
+fi
+if test -z "${OSTREE}"; then
+ cat <<EOF
+ERROR:
+Couldn't find ostree; you can set the OSTREE environment variable to point to it
+e.g.: OSTREE=~user/checkout/ostree/ostree $0
+EOF
+ exit 1
+fi
+
+if test -z "$DRACUT"; then
+ if ! test -d dracut; then
+ cat <<EOF
+Checking out and patching dracut...
+EOF
+ git clone git://git.kernel.org/pub/scm/boot/dracut/dracut.git
+ (cd dracut; git am $SRCDIR/0001-Support-OSTree.patch)
+ (cd dracut; make)
+ fi
+ DRACUT=`pwd`/dracut/dracut
+fi
+
+case `uname -p` in
+ x86_64)
+ ARCH=amd64
+ ;;
+ *)
+ echo "Unsupported architecture"
+ ;;
+esac;
+
+DEBTARGET=wheezy
+
+INITRD_MOVE_MOUNTS="dev proc sys"
+TOPROOT_BIND_MOUNTS="boot home root tmp"
+OSTREE_BIND_MOUNTS="var"
+MOVE_MOUNTS="selinux mnt media"
+READONLY_BIND_MOUNTS="bin etc lib lib32 lib64 sbin usr"
+
+cd ${WORKDIR}
+OBJ=debootstrap-$DEBTARGET
+if ! test -d ${OBJ} ; then
+ echo "Creating $DEBTARGET.img"
+ mkdir -p ${OBJ}.tmp
+ debootstrap --download-only --arch $ARCH $DEBTARGET ${OBJ}.tmp
+ mv ${OBJ}.tmp ${OBJ}
+fi
+
+cd ${WORKDIR}
+OBJ=$DEBTARGET-fs
+if ! test -d ${OBJ}; then
+ rm -rf ${OBJ}.tmp
+ mkdir ${OBJ}.tmp
+
+ cd ${OBJ}.tmp;
+ mkdir -m 0755 $INITRD_MOVE_MOUNTS $TOPROOT_BIND_MOUNTS
+ chmod a=rwxt tmp
+
+ mkdir ostree
+
+ mkdir -p -m 0755 ostree/var/{log,run}
+
+ mkdir ostree/repo
+
+ $OSTREE --repo=ostree/repo init
+
+ BRANCHES=""
+
+ mkdir ostree/worktree
+ cd ostree/worktree
+ mkdir -m 0755 $INITRD_MOVE_MOUNTS $TOPROOT_BIND_MOUNTS $OSTREE_BIND_MOUNTS $READONLY_BIND_MOUNTS $MOVE_MOUNTS sysroot
+ chmod a=rwxt tmp
+ $OSTREE --repo=../repo commit -b gnomeos-filesystem -s "Base filesystem layout"
+ BRANCHES="$BRANCHES gnomeos-filesystem"
+ cd ..
+ rm -rf worktree
+
+ for d in ${WORKDIR}/debootstrap-$DEBTARGET/var/cache/apt/archives/*.deb; do
+ bn=$(basename $d)
+ debname=$(echo $bn | cut -f 1 -d _)
+ debversion=$(echo $bn | cut -f 2 -d _)
+ archivename="archive-${debname}"
+ rm -rf worktree; mkdir worktree;
+ cd worktree;
+ mkdir data;
+ ar x $d;
+ tar -x -z -C data -f data.tar.gz;
+ cd data;
+ $OSTREE --repo=../../repo commit -b "${archivename}" -s "Version ${debversion}"
+ BRANCHES="$BRANCHES $archivename"
+ cd ../..
+ done
+ rm -rf worktree
+
+ $OSTREE --repo=repo compose --out-metadata=./compose-meta worktree $BRANCHES
+ cd worktree
+ $OSTREE --repo=../repo commit --metadata-variant=../compose-meta -b gnomeos -s "Compose of Debian $DEBTARGET"
+ cd ..
+ rm -rf worktree
+
+ cd ${WORKDIR}
+ if test -d ${OBJ}; then
+ mv ${OBJ} ${OBJ}.old
+ fi
+ mv ${OBJ}.tmp ${OBJ}
+ rm -rf ${OBJ}.old
+fi
+
+# TODO download source for above
+# TODO download build dependencies for above
+
+cd ${WORKDIR}
+OBJ=gnomeos-fs
+if ! test -d ${OBJ}; then
+ rm -rf ${OBJ}.tmp
+ cp -al $DEBTARGET-fs ${OBJ}.tmp
+ cd ${OBJ}.tmp/ostree;
+ rm -rf worktree
+ $OSTREE --repo=repo checkout gnomeos worktree
+ cd worktree
+ ${SRCDIR}/debian-setup.sh
+ $OSTREE --repo=../repo commit -b gnomeos -s "Run debian-setup.sh"
+ cd ..
+ rm -rf worktree
+
+ rev=$($OSTREE --repo=repo rev-parse gnomeos);
+ $OSTREE --repo=repo checkout ${rev} gnomeos-${rev}
+ $OSTREE --repo=repo run-triggers gnomeos-${rev}
+ ln -s gnomeos-${rev} current
+
+ cd ${WORKDIR}
+ if test -d ${OBJ}; then
+ mv ${OBJ} ${OBJ}.old
+ fi
+ mv ${OBJ}.tmp ${OBJ}
+ rm -rf ${OBJ}.old
+fi
+
+cd ${WORKDIR}
+cp ${SRCDIR}/ostree_switch_root ${WORKDIR}
+
+kv=`uname -r`
+kernel=/boot/vmlinuz-${kv}
+if ! test -f "${kernel}"; then
+ cat <<EOF
+Failed to find ${kernel}
+EOF
+fi
+
+cd ${WORKDIR}
+OBJ=gnomeos-initrd.img
+VOBJ=gnomeos-initrd-${kv}.img
+if ! test -f ${OBJ}; then
+ rm -f ${OBJ}.tmp ${VOBJ}.tmp
+ $DRACUT -l -v -o plymouth --include `pwd`/ostree_switch_root /sbin/ostree_switch_root ${VOBJ}.tmp
+ mv ${VOBJ}.tmp ${VOBJ}
+ ln -sf ${VOBJ} gnomeos-initrd.img
+fi
--- /dev/null
+#!/bin/sh
+# -*- indent-tabs-mode: nil; -*-
+# Run built image in QEMU
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+set -x
+
+SRCDIR=`dirname $0`
+WORKDIR=`pwd`
+
+if ! test -d gnomeos-fs; then
+ echo "Error: couldn't find gnomeos-fs. Run gnomeos-make-image.sh"
+ exit 1
+fi
+
+OBJ=gnomeos-fs.img
+if (! test -f ${OBJ}) || test gnomeos-fs -nt ${OBJ}; then
+ rm -f ${OBJ}.tmp
+ qemu-img create ${OBJ}.tmp 2G
+ mkfs.ext4 -q -F ${OBJ}.tmp
+ mkdir -p fs
+ umount fs || true
+ mount -o loop ${OBJ}.tmp fs
+ cp -a gnomeos-fs/* fs
+ umount fs
+ mv ${OBJ}.tmp ${OBJ}
+fi
+
+kv=`uname -r`
+kernel=/boot/vmlinuz-${kv}
+
+exec qemu-kvm -kernel ${kernel} -initrd gnomeos-initrd.img -hda gnomeos-fs.img -append "root=/dev/sda ostree=current $1"
--- /dev/null
+/* -*- c-file-style: "linux" -*-
+ * ostree_switch_root.c - switch to new root directory and start init.
+ * Copyright 2011 Colin Walters <walters@verbum.org>
+ *
+ * Based of switch_root.c from util-linux.
+ * Copyright 2002-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Peter Jones <pjones@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ * Colin Walters <walters@verbum.org>
+ */
+#define _GNU_SOURCE
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+
+static int
+perrorv (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+static int
+perrorv (const char *format, ...)
+{
+ va_list args;
+ char buf[PATH_MAX];
+ char *p;
+
+ p = strerror_r (errno, buf, sizeof (buf));
+
+ va_start (args, format);
+
+ vfprintf (stderr, format, args);
+ fprintf (stderr, ": %s\n", p);
+ fflush (stderr);
+
+ va_end (args);
+
+ sleep (3);
+
+ return 0;
+}
+
+/* remove all files/directories below dirName -- don't cross mountpoints */
+static int recursiveRemove(int fd)
+{
+ struct stat rb;
+ DIR *dir;
+ int rc = -1;
+ int dfd;
+
+ if (!(dir = fdopendir(fd))) {
+ perrorv("failed to open directory");
+ goto done;
+ }
+
+ /* fdopendir() precludes us from continuing to use the input fd */
+ dfd = dirfd(dir);
+
+ if (fstat(dfd, &rb)) {
+ perrorv("failed to stat directory");
+ goto done;
+ }
+
+ while(1) {
+ struct dirent *d;
+
+ errno = 0;
+ if (!(d = readdir(dir))) {
+ if (errno) {
+ perrorv("failed to read directory");
+ goto done;
+ }
+ break; /* end of directory */
+ }
+
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (d->d_type == DT_DIR) {
+ struct stat sb;
+
+ if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
+ perrorv("failed to stat %s", d->d_name);
+ continue;
+ }
+
+ /* remove subdirectories if device is same as dir */
+ if (sb.st_dev == rb.st_dev) {
+ int cfd;
+
+ cfd = openat(dfd, d->d_name, O_RDONLY);
+ if (cfd >= 0) {
+ recursiveRemove(cfd);
+ close(cfd);
+ }
+ } else
+ continue;
+ }
+
+ if (unlinkat(dfd, d->d_name,
+ d->d_type == DT_DIR ? AT_REMOVEDIR : 0))
+ perrorv("failed to unlink %s", d->d_name);
+ }
+
+ rc = 0; /* success */
+
+done:
+ if (dir)
+ closedir(dir);
+ return rc;
+}
+
+static int make_readonly(const char *tree)
+{
+ struct stat stbuf;
+ /* Ignore nonexistent directories for now;
+ * some installs won't have e.g. lib64
+ */
+ if (stat (tree, &stbuf) < 0)
+ return 0;
+ if (mount(tree, tree, NULL, MS_BIND, NULL) < 0) {
+ perrorv("Failed to do initial RO bind mount %s", tree);
+ return -1;
+ }
+ if (mount(tree, tree, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) {
+ perrorv("Failed to remount RO bind mount %s", tree);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int switchroot(const char *newroot, const char *subroot)
+{
+ const char *initrd_move_mounts[] = { "/dev", "/proc", "/sys", NULL };
+ const char *toproot_bind_mounts[] = { "/boot", "/home", "/root", "/tmp", NULL };
+ const char *ostree_bind_mounts[] = { "/var", NULL };
+ const char *readonly_bind_mounts[] = { "/bin", "/etc", "/lib",
+ "/lib32", "/lib64", "/sbin",
+ "/usr",
+ NULL };
+ int i;
+ int orig_cfd;
+ int new_cfd;
+ int subroot_cfd;
+ pid_t pid;
+ char subroot_path[PATH_MAX];
+ char srcpath[PATH_MAX];
+ char destpath[PATH_MAX];
+
+ fprintf (stderr, "switching to root %s subroot: %s\n", newroot, subroot);
+
+ orig_cfd = open("/", O_RDONLY);
+ new_cfd = open(newroot, O_RDONLY);
+
+ /* For now just remount the rootfs r/w. Should definitely
+ * handle this better later... (famous last words)
+ */
+ if (mount(newroot, newroot, NULL, MS_REMOUNT, NULL) < 0) {
+ perrorv("failed to remount %s read/write", newroot);
+ return -1;
+ }
+
+ snprintf(subroot_path, sizeof(subroot_path), "%s/ostree/%s", newroot, subroot);
+ subroot_cfd = open(subroot_path, O_RDONLY);
+ if (subroot_cfd < 0) {
+ perrorv("failed to open subroot %s", subroot_path);
+ return -1;
+ }
+
+ for (i = 0; initrd_move_mounts[i] != NULL; i++) {
+ snprintf(destpath, sizeof(destpath), "%s%s", subroot_path, initrd_move_mounts[i]);
+
+ if (mount(initrd_move_mounts[i], destpath, NULL, MS_MOVE, NULL) < 0) {
+ perrorv("failed to move initramfs mount %s to %s",
+ initrd_move_mounts[i], destpath);
+ umount2(initrd_move_mounts[i], MNT_FORCE);
+ }
+ }
+
+ for (i = 0; toproot_bind_mounts[i] != NULL; i++) {
+ snprintf(srcpath, sizeof(srcpath), "%s%s", newroot, toproot_bind_mounts[i]);
+ snprintf(destpath, sizeof(destpath), "%s/%s", subroot_path, toproot_bind_mounts[i]);
+ if (mount(srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0) {
+ perrorv("failed to bind mount (class:toproot) %s to %s", srcpath, destpath);
+ return -1;
+ }
+ }
+
+ for (i = 0; ostree_bind_mounts[i] != NULL; i++) {
+ snprintf(srcpath, sizeof(srcpath), "%s/ostree%s", newroot, ostree_bind_mounts[i]);
+ snprintf(destpath, sizeof(destpath), "%s%s", subroot_path, ostree_bind_mounts[i]);
+ if (mount(srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0) {
+ perrorv("failed to bind mount (class:bind) %s to %s", srcpath, destpath);
+ return -1;
+ }
+ }
+
+ snprintf(destpath, sizeof(destpath), "%s/sysroot", subroot_path);
+ if (mount(newroot, destpath, NULL, MS_BIND, NULL) < 0) {
+ perrorv("Failed bind mount sysroot");
+ return -1;
+ }
+
+ if (chdir(newroot)) {
+ perrorv("failed to change directory to %s", newroot);
+ return -1;
+ }
+
+ if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
+ perrorv("failed to mount moving %s to /", newroot);
+ return -1;
+ }
+
+ if (fchdir (new_cfd) < 0) {
+ perrorv("failed to fchdir back to root");
+ return -1;
+ }
+
+ snprintf(destpath, sizeof(destpath), "ostree/%s", subroot);
+ if (chroot(destpath) < 0) {
+ perrorv("failed to change root to %s", destpath);
+ return -1;
+ }
+
+ if (chdir ("/") < 0) {
+ perrorv("failed to chdir to subroot");
+ return -1;
+ }
+
+ for (i = 0; readonly_bind_mounts[i] != NULL; i++) {
+ if (make_readonly(readonly_bind_mounts[i]) < 0) {
+ return -1;
+ }
+ }
+
+ if (orig_cfd >= 0) {
+ pid = fork();
+ if (pid <= 0) {
+ recursiveRemove(orig_cfd);
+ if (pid == 0)
+ exit(EXIT_SUCCESS);
+ }
+ close(orig_cfd);
+ }
+ close(new_cfd);
+ return 0;
+}
+
+static void usage(FILE *output)
+{
+ fprintf(output, "usage: %s <newrootdir> <init> <args to init>\n",
+ program_invocation_short_name);
+ exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ char *newroot, *subroot, *init, **initargs;
+
+ fflush (stderr);
+ if (argc < 4)
+ usage(stderr);
+
+ if ((!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
+ usage(stdout);
+
+ newroot = argv[1];
+ subroot = argv[2];
+ init = argv[3];
+ initargs = &argv[3];
+
+ if (!*newroot || !*subroot || !*init)
+ usage(stderr);
+
+ if (switchroot(newroot, subroot))
+ exit(1);
+
+ if (access(init, X_OK))
+ perrorv("cannot access %s", init);
+
+ execv(init, initargs);
+ perrorv("Failed to exec init '%s'", init);
+ exit(1);
+}
+
+++ /dev/null
-ostree_switch_root
+++ /dev/null
-From e9cb0f0c6ef13773c823610f27a562bd54b2acd3 Mon Sep 17 00:00:00 2001
-From: Colin Walters <walters@verbum.org>
-Date: Mon, 24 Oct 2011 22:01:17 -0400
-Subject: [PATCH] Support OSTree
-
-See http://git.gnome.org/browse/ostree
----
- modules.d/95rootfs-block/mount-root.sh | 12 ++++++------
- modules.d/99base/init | 32 ++++++++++++++++++++++----------
- 2 files changed, 28 insertions(+), 16 deletions(-)
-
-diff --git a/modules.d/95rootfs-block/mount-root.sh b/modules.d/95rootfs-block/mount-root.sh
-index 2c89431..4f411da 100755
---- a/modules.d/95rootfs-block/mount-root.sh
-+++ b/modules.d/95rootfs-block/mount-root.sh
-@@ -34,8 +34,8 @@ mount_root() {
-
- READONLY=
- fsckoptions=
-- if [ -f "$NEWROOT"/etc/sysconfig/readonly-root ]; then
-- . "$NEWROOT"/etc/sysconfig/readonly-root
-+ if [ -f "$NEWROOT_PREFIX"/etc/sysconfig/readonly-root ]; then
-+ . "$NEWROOT_PREFIX"/etc/sysconfig/readonly-root
- fi
-
- if getargbool 0 "readonlyroot=" -y readonlyroot; then
-@@ -57,7 +57,7 @@ mount_root() {
- if [ -f "$NEWROOT"/forcefsck ] || getargbool 0 forcefsck ; then
- fsckoptions="-f $fsckoptions"
- elif [ -f "$NEWROOT"/.autofsck ]; then
-- [ -f "$NEWROOT"/etc/sysconfig/autofsck ] && . "$NEWROOT"/etc/sysconfig/autofsck
-+ [ -f "$NEWROOT_PREFIX"/etc/sysconfig/autofsck ] && . "$NEWROOT_PREFIX"/etc/sysconfig/autofsck
- if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then
- AUTOFSCK_OPT="$AUTOFSCK_OPT -f"
- fi
-@@ -73,8 +73,8 @@ mount_root() {
- rootopts=
- if getargbool 1 rd.fstab -n rd_NO_FSTAB \
- && ! getarg rootflags \
-- && [ -f "$NEWROOT/etc/fstab" ] \
-- && ! [ -L "$NEWROOT/etc/fstab" ]; then
-+ && [ -f "$NEWROOT_PREFIX/etc/fstab" ] \
-+ && ! [ -L "$NEWROOT_PREFIX/etc/fstab" ]; then
- # if $NEWROOT/etc/fstab contains special mount options for
- # the root filesystem,
- # remount it with the proper options
-@@ -89,7 +89,7 @@ mount_root() {
- rootopts=$opts
- break
- fi
-- done < "$NEWROOT/etc/fstab"
-+ done < "$NEWROOT_PREFIX/etc/fstab"
-
- rootopts=$(filter_rootopts $rootopts)
- fi
-diff --git a/modules.d/99base/init b/modules.d/99base/init
-index 36b2152..9636990 100755
---- a/modules.d/99base/init
-+++ b/modules.d/99base/init
-@@ -275,6 +275,13 @@ unset queuetriggered
- unset main_loop
- unset RDRETRY
-
-+ostree=$(getarg ostree=)
-+if test -n $ostree; then
-+ NEWROOT_PREFIX=${NEWROOT}/ostree/$ostree
-+else
-+ NEWROOT_PREFIX=$NEWROOT
-+fi
-+
- # pre-mount happens before we try to mount the root filesystem,
- # and happens once.
- getarg 'rd.break=pre-mount' 'rdbreak=pre-mount' && emergency_shell -n pre-mount "Break pre-mount"
-@@ -287,14 +294,15 @@ getarg 'rd.break=mount' 'rdbreak=mount' && emergency_shell -n mount "Break mount
- i=0
- while :; do
- if ismounted "$NEWROOT"; then
-- usable_root "$NEWROOT" && break;
-+ echo "Checking usable $NEWROOT_PREFIX"
-+ usable_root "$NEWROOT_PREFIX" && break;
- umount "$NEWROOT"
- fi
- for f in $hookdir/mount/*.sh; do
- [ -f "$f" ] && . "$f"
- if ismounted "$NEWROOT"; then
-- usable_root "$NEWROOT" && break;
-- warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook"
-+ usable_root "$NEWROOT_PREFIX" && break;
-+ warn "$NEWROOT_PREFIX has no proper rootfs layout, ignoring and removing offending mount hook"
- umount "$NEWROOT"
- rm -f "$f"
- fi
-@@ -320,11 +328,11 @@ unset __usr_found
- for i in "$(getarg real_init=)" "$(getarg init=)" /sbin/init /etc/init /init /bin/sh; do
- [ -n "$i" ] || continue
-
-- __p=$(readlink -m "$NEWROOT$i")
-+ __p=$(readlink -m "$NEWROOT_PREFIX$i")
- if [ -n "$__p" ] \
- && [ "x$__usr_found" = "x" ] \
- && [ ! -x "$__p" ] \
-- && strstr "$__p" "$NEWROOT/usr" \
-+ && strstr "$__p" "$NEWROOT_PREFIX/usr" \
- ; then
- # we have to mount /usr
- while read dev mp fs opts rest; do
-@@ -333,14 +341,14 @@ for i in "$(getarg real_init=)" "$(getarg init=)" /sbin/init /etc/init /init /bi
- __usr_found="1"
- break
- fi
-- done < "$NEWROOT/etc/fstab" >> /etc/fstab
-+ done < "$NEWROOT_PREFIX/etc/fstab" >> /etc/fstab
- if [ "x$__usr_found" != "x" ]; then
- info "Mounting /usr"
-- mount "$NEWROOT/usr" 2>&1 | vinfo
-+ mount "$NEWROOT_PREFIX/usr" 2>&1 | vinfo
- fi
- fi
-
-- __p=$(readlink -f "$NEWROOT$i")
-+ __p=$(readlink -f "$NEWROOT_PREFIX$i")
- if [ -x "$__p" ]; then
- INIT="$i"
- break
-@@ -445,7 +453,11 @@ info "Switching root"
- unset PS4
-
- CAPSH=$(command -v capsh)
--SWITCH_ROOT=$(command -v switch_root)
-+if test -n "${ostree}"; then
-+ SWITCH_ROOT=$(command -v ostree_switch_root)
-+else
-+ SWITCH_ROOT=$(command -v switch_root)
-+fi
- PATH=$OLDPATH
- export PATH
-
-@@ -463,7 +475,7 @@ if [ -f /etc/capsdrop ]; then
- }
- else
- unset RD_DEBUG
-- exec $SWITCH_ROOT "$NEWROOT" "$INIT" $initargs || {
-+ exec $SWITCH_ROOT "$NEWROOT" $ostree "$INIT" $initargs || {
- warn "Something went very badly wrong in the initramfs. Please "
- warn "file a bug against dracut."
- emergency_shell
---
-1.7.6.4
-
+++ /dev/null
-all: ostree_switch_root
-
-ostree_switch_root: ostree_switch_root.c Makefile
- gcc -Wall -o $@ $<
+++ /dev/null
-
-Experimenting with multiple roots
----------------------------------
-
- $ mkdir gnomeos-chroot
- $ qemu-img create gnomeos.raw 2G
- $ mkfs.ext2 -F gnomeos.raw
- $ mount -o loop gnomeos.raw gnomeos-chroot
- $ debootstrap --arch=amd64 squeeze gnomeos-chroot
-
-<http://wiki.debian.org/QEMU#Setting_up_a_testing.2BAC8-unstable_system>
-
-Follow the steps for making a disk image, downloading the business
-card CD, booting it in QEMU and running through the installer. Note I
-used the QCOW format, since it is more efficient. Here are the steps
-I chose:
-
- $ qemu-img create -f qcow2 debian.qcow 2G
- $ qemu-kvm -hda debian.qcow -cdrom debian-testing-amd64-businesscard.iso -boot d -m 512
-
-Test that the image works after installation too, before you start
-modifying things below! Remember to remove the -cdrom and -boot
-options from the installation QEMU command. It should just look like
-this:
-
-$ qemu-kvm -hda debian.qcow -m 512
-
-
-Modifying the image
--------------------
-
-You now have a disk image in debian.img, and the first partition
-should be ext4.
-
-The first thing I did was mount the image, and move the "read only"
-parts of the OS to a new directory "r0".
-
- $ mkdir /mnt/debian
- $ modprobe nbd max_part=8
- $ qemu-nbd --connect=/dev/nbd0 debian.qcow
- $ mount /dev/nbd0p1 /mnt/debian/
- $ cd /mnt/debian
- $ mkdir r0
- $ DIRS="bin dev etc lib lib32 lib64 media mnt opt proc root run sbin selinux srv sys tmp usr"
- $ mv $DIRS r0
- $ mkdir r0/{boot,var,home}
- $ touch r0/{boot,var,home}/EMPTY
-
-Note that /boot, /home and /var are left shared; we create empty
-destination directories that will be mounted over. Now with it still
-mounted, we need to move on to the next part - modifying the initrd.
-
-Then I started hacking on the initrd, making understand how to chroot
-to "r0". I ended up with two patches - one to util-linux, and one to
-the "init" script in Debian's initrd.
-
-See:
- 0001-switch_root-Add-subroot-option.patch
- 0001-Add-support-for-subroot-option.patch
-
-$ git clone --depth=1 git://github.com/karelzak/util-linux.git
-$ cd util-linux
-$ patch -p1 -i ../0001-switch_root-Add-subroot-option.patch
-$ ./autogen.sh; ./configure ; make
-
-Now you have a modified "sys-utils/switch_root" binary. Let's next
-patch the initrd and rebuild it:
-
-$ cd ..
-
-Make a backup:
-
- $ mkdir initrd
- $ cp /mnt/debian/boot/initrd.img-3.0.0-1-amd64{,.orig}
-
-Unpack, and patch:
-
- $ zcat /mnt/debian/boot/initrd.img-3.0.0-1-amd64 | (cd initrd; cpio -d -i -v)
- $ (cd initrd && patch -p1 -i ../0001-Add-support-for-subroot-option.patch)
-
-Repack:
-
- $ (cd initrd; find | cpio -o -H newc) | gzip > /mnt/debian/boot/initrd.img-3.0.0-1-amd64.new
- $ mv /mnt/debian/boot/initrd.img-3.0.0-1-amd64{.new,}
-
-Unmount:
-
- $ umount /mnt/debian
-
-Running hacktree inside the system
-----------------------------------
-
-This means that after booting, every process would be in /r0 -
-including any hacktree process. Assuming objects live in say
-/objects, we need some way for hacktree to switch things. I think
-just chroot breakout would work. This has the advantage the daemon
-can continue to use libraries from the active host.
-
-Note there is a self-reference here (as is present in Debian/Fedora
-etc.) - the update system would at present be shipped with the system
-itself. Should they be independent? That has advantages and
-disadvantages. I think we should just try really really hard to avoid
-breaking hacktree in updates.
-
+++ /dev/null
-#!/bin/sh
-# This script sets up things we want to ship with the OS tree. It should
-# NOT set up caches. For example, do NOT run ldconfig in here.
-
-set -e
-set -x
-
-echo gnomeos >./etc/hostname
-
-cat >./etc/default/locale <<EOF
-LANG="en_US.UTF-8"
-EOF
-
-cp -p ./usr/share/sysvinit/inittab ./etc/inittab
-cp -p ./usr/share/base-files/nsswitch.conf ./etc/nsswitch.conf
-
-cat >./etc/pam.d/common-account <<EOF
-account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
-account requisite pam_deny.so
-account required pam_permit.so
-EOF
-cat >./etc/pam.d/common-auth <<EOF
-auth [success=1 default=ignore] pam_unix.so nullok_secure
-auth requisite pam_deny.so
-auth required pam_permit.so
-EOF
-cat >./etc/pam.d/common-password <<EOF
-password [success=1 default=ignore] pam_unix.so obscure sha512
-password requisite pam_deny.so
-password required pam_permit.so
-EOF
-cat >./etc/pam.d/common-session <<EOF
-session [default=1] pam_permit.so
-session requisite pam_deny.so
-session required pam_permit.so
-session required pam_unix.so
-EOF
-
-# base-passwd
-cp -p ./usr/share/base-passwd/passwd.master ./etc/passwd
-cp -p ./usr/share/base-passwd/group.master ./etc/group
-
-# Service rc.d defaults
-setuprc () {
- name=$1
- shift
- type=$1
- shift
- priority=$1
- shift
-
- for x in $@; do
- ln -s ../init.d/$name ./etc/rc${x}.d/${type}${priority}${name}
- done
-}
-
-setuprc rsyslog S 10 2 3 4 5
-setuprc rsyslog S 30 0 6
-setuprc rsyslog K 90 1
-setuprc cron S 89 2 3 4 5
+++ /dev/null
-#!/bin/sh
-# -*- indent-tabs-mode: nil; -*-
-# Install built ostree image to current system
-#
-# Copyright (C) 2011 Colin Walters <walters@verbum.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-set -e
-set -x
-
-SRCDIR=`dirname $0`
-WORKDIR=`pwd`
-
-OBJ=gnomeos-initrd.img
-if ! test -f ${OBJ}; then
- echo "Error: couldn't find '$OBJ'. Run gnomeos-make-image.sh"
- exit 1
-fi
-
-if test -d /ostree; then
- echo "/ostree already exists"
- exit 1
-fi
-
-cp -a gnomeos-fs/ostree /
-initrd=`readlink gnomeos-initrd.img`
-cp ${initrd} /boot
-grubby --title "GNOME OS" --add-kernel=$kernel --copy-default --initrd=/boot/${initrd}
+++ /dev/null
-#!/bin/sh
-# -*- indent-tabs-mode: nil; -*-
-# Generate a root filesystem image
-#
-# Copyright (C) 2011 Colin Walters <walters@verbum.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-set -e
-set -x
-
-SRCDIR=`dirname $0`
-WORKDIR=`pwd`
-
-DEPENDS="debootstrap"
-
-for x in $DEPENDS; do
- if ! command -v $x; then
- cat <<EOF
-Couldn't find required dependency $x
-EOF
- exit 1
- fi
-done
-
-if test $(id -u) == 0; then
- echo "Should not run this script as root."
- exit 1
-fi
-
-if test -z "${OSTREE}"; then
- OSTREE=`command -v ostree || true`
-fi
-if test -z "${OSTREE}"; then
- cat <<EOF
-ERROR:
-Couldn't find ostree; you can set the OSTREE environment variable to point to it
-e.g.: OSTREE=~user/checkout/ostree/ostree $0
-EOF
- exit 1
-fi
-
-if test -z "$DRACUT"; then
- if ! test -d dracut; then
- cat <<EOF
-Checking out and patching dracut...
-EOF
- git clone git://git.kernel.org/pub/scm/boot/dracut/dracut.git
- (cd dracut; git am $SRCDIR/0001-Support-OSTree.patch)
- (cd dracut; make)
- fi
- DRACUT=`pwd`/dracut/dracut
-fi
-
-case `uname -p` in
- x86_64)
- ARCH=amd64
- ;;
- *)
- echo "Unsupported architecture"
- ;;
-esac;
-
-DEBTARGET=wheezy
-
-INITRD_MOVE_MOUNTS="dev proc sys"
-TOPROOT_BIND_MOUNTS="boot home root tmp"
-OSTREE_BIND_MOUNTS="var"
-MOVE_MOUNTS="selinux mnt media"
-READONLY_BIND_MOUNTS="bin etc lib lib32 lib64 sbin usr"
-
-cd ${WORKDIR}
-OBJ=debootstrap-$DEBTARGET
-if ! test -d ${OBJ} ; then
- echo "Creating $DEBTARGET.img"
- mkdir -p ${OBJ}.tmp
- debootstrap --download-only --arch $ARCH $DEBTARGET ${OBJ}.tmp
- mv ${OBJ}.tmp ${OBJ}
-fi
-
-cd ${WORKDIR}
-OBJ=$DEBTARGET-fs
-if ! test -d ${OBJ}; then
- rm -rf ${OBJ}.tmp
- mkdir ${OBJ}.tmp
-
- cd ${OBJ}.tmp;
- mkdir -m 0755 $INITRD_MOVE_MOUNTS $TOPROOT_BIND_MOUNTS
- chmod a=rwxt tmp
-
- mkdir ostree
-
- mkdir -p -m 0755 ostree/var/{log,run}
-
- mkdir ostree/repo
-
- $OSTREE --repo=ostree/repo init
-
- BRANCHES=""
-
- mkdir ostree/worktree
- cd ostree/worktree
- mkdir -m 0755 $INITRD_MOVE_MOUNTS $TOPROOT_BIND_MOUNTS $OSTREE_BIND_MOUNTS $READONLY_BIND_MOUNTS $MOVE_MOUNTS sysroot
- chmod a=rwxt tmp
- $OSTREE --repo=../repo commit -b gnomeos-filesystem -s "Base filesystem layout"
- BRANCHES="$BRANCHES gnomeos-filesystem"
- cd ..
- rm -rf worktree
-
- for d in ${WORKDIR}/debootstrap-$DEBTARGET/var/cache/apt/archives/*.deb; do
- bn=$(basename $d)
- debname=$(echo $bn | cut -f 1 -d _)
- debversion=$(echo $bn | cut -f 2 -d _)
- archivename="archive-${debname}"
- rm -rf worktree; mkdir worktree;
- cd worktree;
- mkdir data;
- ar x $d;
- tar -x -z -C data -f data.tar.gz;
- cd data;
- $OSTREE --repo=../../repo commit -b "${archivename}" -s "Version ${debversion}"
- BRANCHES="$BRANCHES $archivename"
- cd ../..
- done
- rm -rf worktree
-
- $OSTREE --repo=repo compose --out-metadata=./compose-meta worktree $BRANCHES
- cd worktree
- $OSTREE --repo=../repo commit --metadata-variant=../compose-meta -b gnomeos -s "Compose of Debian $DEBTARGET"
- cd ..
- rm -rf worktree
-
- cd ${WORKDIR}
- if test -d ${OBJ}; then
- mv ${OBJ} ${OBJ}.old
- fi
- mv ${OBJ}.tmp ${OBJ}
- rm -rf ${OBJ}.old
-fi
-
-# TODO download source for above
-# TODO download build dependencies for above
-
-cd ${WORKDIR}
-OBJ=gnomeos-fs
-if ! test -d ${OBJ}; then
- rm -rf ${OBJ}.tmp
- cp -al $DEBTARGET-fs ${OBJ}.tmp
- cd ${OBJ}.tmp/ostree;
- rm -rf worktree
- $OSTREE --repo=repo checkout gnomeos worktree
- cd worktree
- ${SRCDIR}/debian-setup.sh
- $OSTREE --repo=../repo commit -b gnomeos -s "Run debian-setup.sh"
- cd ..
- rm -rf worktree
-
- rev=$($OSTREE --repo=repo rev-parse gnomeos);
- $OSTREE --repo=repo checkout ${rev} gnomeos-${rev}
- $OSTREE --repo=repo run-triggers gnomeos-${rev}
- ln -s gnomeos-${rev} current
-
- cd ${WORKDIR}
- if test -d ${OBJ}; then
- mv ${OBJ} ${OBJ}.old
- fi
- mv ${OBJ}.tmp ${OBJ}
- rm -rf ${OBJ}.old
-fi
-
-cd ${WORKDIR}
-cp ${SRCDIR}/ostree_switch_root ${WORKDIR}
-
-kv=`uname -r`
-kernel=/boot/vmlinuz-${kv}
-if ! test -f "${kernel}"; then
- cat <<EOF
-Failed to find ${kernel}
-EOF
-fi
-
-cd ${WORKDIR}
-OBJ=gnomeos-initrd.img
-VOBJ=gnomeos-initrd-${kv}.img
-if ! test -f ${OBJ}; then
- rm -f ${OBJ}.tmp ${VOBJ}.tmp
- $DRACUT -l -v -o plymouth --include `pwd`/ostree_switch_root /sbin/ostree_switch_root ${VOBJ}.tmp
- mv ${VOBJ}.tmp ${VOBJ}
- ln -sf ${VOBJ} gnomeos-initrd.img
-fi
+++ /dev/null
-#!/bin/sh
-# -*- indent-tabs-mode: nil; -*-
-# Run built image in QEMU
-#
-# Copyright (C) 2011 Colin Walters <walters@verbum.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-set -e
-set -x
-
-SRCDIR=`dirname $0`
-WORKDIR=`pwd`
-
-if ! test -d gnomeos-fs; then
- echo "Error: couldn't find gnomeos-fs. Run gnomeos-make-image.sh"
- exit 1
-fi
-
-OBJ=gnomeos-fs.img
-if (! test -f ${OBJ}) || test gnomeos-fs -nt ${OBJ}; then
- rm -f ${OBJ}.tmp
- qemu-img create ${OBJ}.tmp 2G
- mkfs.ext4 -q -F ${OBJ}.tmp
- mkdir -p fs
- umount fs || true
- mount -o loop ${OBJ}.tmp fs
- cp -a gnomeos-fs/* fs
- umount fs
- mv ${OBJ}.tmp ${OBJ}
-fi
-
-kv=`uname -r`
-kernel=/boot/vmlinuz-${kv}
-
-exec qemu-kvm -kernel ${kernel} -initrd gnomeos-initrd.img -hda gnomeos-fs.img -append "root=/dev/sda ostree=current $1"
+++ /dev/null
-/* -*- c-file-style: "linux" -*-
- * ostree_switch_root.c - switch to new root directory and start init.
- * Copyright 2011 Colin Walters <walters@verbum.org>
- *
- * Based of switch_root.c from util-linux.
- * Copyright 2002-2009 Red Hat, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Authors:
- * Peter Jones <pjones@redhat.com>
- * Jeremy Katz <katzj@redhat.com>
- * Colin Walters <walters@verbum.org>
- */
-#define _GNU_SOURCE
-#include <sys/mount.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/param.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-#include <dirent.h>
-
-static int
-perrorv (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
-
-static int
-perrorv (const char *format, ...)
-{
- va_list args;
- char buf[PATH_MAX];
- char *p;
-
- p = strerror_r (errno, buf, sizeof (buf));
-
- va_start (args, format);
-
- vfprintf (stderr, format, args);
- fprintf (stderr, ": %s\n", p);
- fflush (stderr);
-
- va_end (args);
-
- sleep (3);
-
- return 0;
-}
-
-/* remove all files/directories below dirName -- don't cross mountpoints */
-static int recursiveRemove(int fd)
-{
- struct stat rb;
- DIR *dir;
- int rc = -1;
- int dfd;
-
- if (!(dir = fdopendir(fd))) {
- perrorv("failed to open directory");
- goto done;
- }
-
- /* fdopendir() precludes us from continuing to use the input fd */
- dfd = dirfd(dir);
-
- if (fstat(dfd, &rb)) {
- perrorv("failed to stat directory");
- goto done;
- }
-
- while(1) {
- struct dirent *d;
-
- errno = 0;
- if (!(d = readdir(dir))) {
- if (errno) {
- perrorv("failed to read directory");
- goto done;
- }
- break; /* end of directory */
- }
-
- if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
- continue;
-
- if (d->d_type == DT_DIR) {
- struct stat sb;
-
- if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
- perrorv("failed to stat %s", d->d_name);
- continue;
- }
-
- /* remove subdirectories if device is same as dir */
- if (sb.st_dev == rb.st_dev) {
- int cfd;
-
- cfd = openat(dfd, d->d_name, O_RDONLY);
- if (cfd >= 0) {
- recursiveRemove(cfd);
- close(cfd);
- }
- } else
- continue;
- }
-
- if (unlinkat(dfd, d->d_name,
- d->d_type == DT_DIR ? AT_REMOVEDIR : 0))
- perrorv("failed to unlink %s", d->d_name);
- }
-
- rc = 0; /* success */
-
-done:
- if (dir)
- closedir(dir);
- return rc;
-}
-
-static int make_readonly(const char *tree)
-{
- struct stat stbuf;
- /* Ignore nonexistent directories for now;
- * some installs won't have e.g. lib64
- */
- if (stat (tree, &stbuf) < 0)
- return 0;
- if (mount(tree, tree, NULL, MS_BIND, NULL) < 0) {
- perrorv("Failed to do initial RO bind mount %s", tree);
- return -1;
- }
- if (mount(tree, tree, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) {
- perrorv("Failed to remount RO bind mount %s", tree);
- return -1;
- }
- return 0;
-}
-
-
-static int switchroot(const char *newroot, const char *subroot)
-{
- const char *initrd_move_mounts[] = { "/dev", "/proc", "/sys", NULL };
- const char *toproot_bind_mounts[] = { "/boot", "/home", "/root", "/tmp", NULL };
- const char *ostree_bind_mounts[] = { "/var", NULL };
- const char *readonly_bind_mounts[] = { "/bin", "/etc", "/lib",
- "/lib32", "/lib64", "/sbin",
- "/usr",
- NULL };
- int i;
- int orig_cfd;
- int new_cfd;
- int subroot_cfd;
- pid_t pid;
- char subroot_path[PATH_MAX];
- char srcpath[PATH_MAX];
- char destpath[PATH_MAX];
-
- fprintf (stderr, "switching to root %s subroot: %s\n", newroot, subroot);
-
- orig_cfd = open("/", O_RDONLY);
- new_cfd = open(newroot, O_RDONLY);
-
- /* For now just remount the rootfs r/w. Should definitely
- * handle this better later... (famous last words)
- */
- if (mount(newroot, newroot, NULL, MS_REMOUNT, NULL) < 0) {
- perrorv("failed to remount %s read/write", newroot);
- return -1;
- }
-
- snprintf(subroot_path, sizeof(subroot_path), "%s/ostree/%s", newroot, subroot);
- subroot_cfd = open(subroot_path, O_RDONLY);
- if (subroot_cfd < 0) {
- perrorv("failed to open subroot %s", subroot_path);
- return -1;
- }
-
- for (i = 0; initrd_move_mounts[i] != NULL; i++) {
- snprintf(destpath, sizeof(destpath), "%s%s", subroot_path, initrd_move_mounts[i]);
-
- if (mount(initrd_move_mounts[i], destpath, NULL, MS_MOVE, NULL) < 0) {
- perrorv("failed to move initramfs mount %s to %s",
- initrd_move_mounts[i], destpath);
- umount2(initrd_move_mounts[i], MNT_FORCE);
- }
- }
-
- for (i = 0; toproot_bind_mounts[i] != NULL; i++) {
- snprintf(srcpath, sizeof(srcpath), "%s%s", newroot, toproot_bind_mounts[i]);
- snprintf(destpath, sizeof(destpath), "%s/%s", subroot_path, toproot_bind_mounts[i]);
- if (mount(srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0) {
- perrorv("failed to bind mount (class:toproot) %s to %s", srcpath, destpath);
- return -1;
- }
- }
-
- for (i = 0; ostree_bind_mounts[i] != NULL; i++) {
- snprintf(srcpath, sizeof(srcpath), "%s/ostree%s", newroot, ostree_bind_mounts[i]);
- snprintf(destpath, sizeof(destpath), "%s%s", subroot_path, ostree_bind_mounts[i]);
- if (mount(srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0) {
- perrorv("failed to bind mount (class:bind) %s to %s", srcpath, destpath);
- return -1;
- }
- }
-
- snprintf(destpath, sizeof(destpath), "%s/sysroot", subroot_path);
- if (mount(newroot, destpath, NULL, MS_BIND, NULL) < 0) {
- perrorv("Failed bind mount sysroot");
- return -1;
- }
-
- if (chdir(newroot)) {
- perrorv("failed to change directory to %s", newroot);
- return -1;
- }
-
- if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
- perrorv("failed to mount moving %s to /", newroot);
- return -1;
- }
-
- if (fchdir (new_cfd) < 0) {
- perrorv("failed to fchdir back to root");
- return -1;
- }
-
- snprintf(destpath, sizeof(destpath), "ostree/%s", subroot);
- if (chroot(destpath) < 0) {
- perrorv("failed to change root to %s", destpath);
- return -1;
- }
-
- if (chdir ("/") < 0) {
- perrorv("failed to chdir to subroot");
- return -1;
- }
-
- for (i = 0; readonly_bind_mounts[i] != NULL; i++) {
- if (make_readonly(readonly_bind_mounts[i]) < 0) {
- return -1;
- }
- }
-
- if (orig_cfd >= 0) {
- pid = fork();
- if (pid <= 0) {
- recursiveRemove(orig_cfd);
- if (pid == 0)
- exit(EXIT_SUCCESS);
- }
- close(orig_cfd);
- }
- close(new_cfd);
- return 0;
-}
-
-static void usage(FILE *output)
-{
- fprintf(output, "usage: %s <newrootdir> <init> <args to init>\n",
- program_invocation_short_name);
- exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
-}
-
-int main(int argc, char *argv[])
-{
- char *newroot, *subroot, *init, **initargs;
-
- fflush (stderr);
- if (argc < 4)
- usage(stderr);
-
- if ((!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
- usage(stdout);
-
- newroot = argv[1];
- subroot = argv[2];
- init = argv[3];
- initargs = &argv[3];
-
- if (!*newroot || !*subroot || !*init)
- usage(stderr);
-
- if (switchroot(newroot, subroot))
- exit(1);
-
- if (access(init, X_OK))
- perrorv("cannot access %s", init);
-
- execv(init, initargs);
- perrorv("Failed to exec init '%s'", init);
- exit(1);
-}
-